Skip to content

fix(types): key the grid and report zod mirrors by their own declaration vocabulary (objectui#8516, objectui#8556) - #8573

Merged
os-sales merged 4 commits into
mainfrom
claude/issue-8516-mirror-partial-record-narrowing
Sep 8, 2026
Merged

fix(types): key the grid and report zod mirrors by their own declaration vocabulary (objectui#8516, objectui#8556)#8573
os-sales merged 4 commits into
mainfrom
claude/issue-8516-mirror-partial-record-narrowing

Conversation

@claude

@claude claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #8516
Fixes #8556

⚠️ Notation. Generic type arguments are written with SQUARE brackets throughout — Partial[Record[BreakpointName, number]], not the real spelling. AGENTS.md records that GitHub silently deletes tag-shaped fragments on save, backticks and fenced blocks included, and this PR is entirely about the shape of a type.

What changed

Two mirrors in @object-ui/types/zod restated a CLOSED key set as an OPEN one, so the validator that judges authored JSON accepted a spelling its own published declaration refuses. One defect class, one repair, landed together.

key mirror was mirror is declaration
GridSchema.columns (zod/layout.zod.ts) z.record(z.string(), z.number()) z.partialRecord over the six breakpoints number OR Partial[Record[BreakpointName, number]] (objectui#8505)
ReportComponentSchema.exportConfigs (zod/reports.zod.ts) z.record(z.string(), ReportExportConfigSchema) z.partialRecord(ReportExportFormatSchema, …) Partial[Record[ReportExportFormat, ReportExportConfig]] (objectui#6121)

Not z.record(z.enum([…]), …). Measured here on zod 4.4.3, not quoted: that spelling makes zod require every member, so { md: 2 } fails with five invalid_type issues, one per absent key. It would trade each divergence for its exact opposite. The measurement is now an executable pin rather than folklore, and the firing control below reproduces it.

The three shape questions, answered by measurement

Q1 — refuse or strip, and which diagnostic does an author actually see? Both candidates have the right accept set; they differ only in where the bad key surfaces. Measured through the real printer path (columns is an undiscriminated union, so zod emits one top-level invalid_union whose message is a bare "Invalid input"; @object-ui/cli's explainUnionIssue expands the arms and rebases their paths):

spelling what os-ui validate prints
z.object of six optionals + .strict() Unrecognized key: "xxl" · Path: columns · Code: unrecognized_keys
z.partialRecord (chosen) Invalid key in record · Path: columns → xxl · Code: invalid_key

Both name xxl; .strict() puts it in the message, partialRecord puts it in the path. Chosen z.partialRecord on three readings the diagnostic does not decide: its inferred input type is exactly the declaration (pinned invariantly, both directions, as _columnsFace); it reuses the already-exported ReportExportFormatSchema verbatim on the second key, where .strict() would hand-restate five format names; and it avoids writing a seventh hand-copy of the six-member breakpoint object, which the triage comment on #8516 explicitly warned against. Contract review added a fourth reading this PR had not claimed: z.partialRecord clones its key schema, so reusing the shared ReportExportFormatSchema here leaves that const's own _zod.values intact and defaultExportFormat: 'xml' on the same node is still refused. Both spellings REFUSE rather than strip — BaseSchema is .passthrough(), but columns is a declared member.

Q2 — breaking for stored metadata? Measured across every tracked file in this repo and the objectstack sibling checkout, with test fixtures and changeset prose separated out rather than folded into the authored total:

category grid nodes with an object columns out of vocabulary
authored docs (content/docs, examples/schema-catalog, skills/objectui) 9 0
app/site source (apps/site) 1 0
authored total 10 0
test fixtures 27 7
changeset prose quoting the defect 1 1
objectstack sibling checkout 0 0

exportConfigs: one authored sitecontent/docs/core/report-schema.mdx:372, keyed pdf / excel / csv, all in vocabulary. None in objectstack.

⇒ This narrowing refuses zero documents that exist today, and the ones it would refuse are already rendering wrong. Graded minor with the break spelled out, per objectui#8485's precedent; this repo refuses major.

⚠️ Two figures here were wrong in the first draft of this PR, and both are corrected above rather than quietly restated. (a) exportConfigs was reported as having zero authored inhabitants. The scan that produced that zero could not have found a true positive: its pattern required the value's braces to contain no nested braces, and every real exportConfigs contains per-format objects — an unfalsifiable zero of exactly the shape AGENTS.md warns about. Re-run brace-balanced, it is one, and in vocabulary, so the conclusion is unchanged. (b) The authored columns total was given as 28, which was both an arithmetic slip and a category error: it subtracted the out-of-vocabulary sites from the grand total and then called the remainder "authored", silently counting renderer and type test fixtures as authored documents. The defined authored total is 10.

⚠️ On the reviewer's count of 17. ⛔ Not silently adopted, and ⛔ not defended — my 28 was wrong on its own terms, independently of theirs. I cannot reproduce 17 from here because the review's counting rule is not stated in a form I can re-run, and 17 sits between my authored total (10) and my grand total (38 on this branch, 33 on main), so it is most likely a third partition of the same population — plausibly non-test files plus some fixtures, or the main tree rather than this branch. The number above is stated with its method (git ls-files over both trees, brace-balanced extraction, a type: 'grid' requirement, categorised by path) so it can be re-run and disagreed with precisely. All three readings agree on the load-bearing fact, which is the one the grade rests on: zero authored documents carry an out-of-vocabulary key.

Q3 — does the sibling responsive-grid mirror move? No, and it is left alone. @object-ui/layout's BreakpointColumnMap (packages/layout/src/ResponsiveGrid.tsx) already declares exactly the six xs2xl optional numbers by hand and already agrees. It is also not a mirror in the relevant sense: packages/layout/src/ contains no .zod.ts file at all and ResponsiveGrid.tsx imports no zod — it is a React props type, not an authoring face. Nothing to move.

Hypotheses — one falsified, one unexercised, one confirmed

H1 — "the ledger already SEES both keys" is FALSE. PR #8553 has not landed: it is still open, merged: false, against main. So the repaired WiderThanDeclared operator is not on main, the pre-repair operator still reads an index-signature record and a partial record over a key union as mutually assignable, and neither key has, or ever had, a ledger entry. Both cards' framing and the dispatch's serial constraint were written expecting the opposite.

The falsification is not just a paperwork note — it is visible in the firing control: removing this repair reddens 7 runtime pins and 5 compile-time ones but leaves zod-mirror-parity.test.ts green, which is precisely the blindness objectui#8553 was opened to repair.

H2 — the ledger counter conflict never arose. The dispatch cleared a serial constraint on the premise that both cards edit zod-mirror-parity.test.ts and would collide with PR #8501's patch round. Neither card required editing that file, because neither key had an entry to move. Re-derived with the file's OWN AST instrument (ledgerEntryKeys, the same walk the file runs on itself) at origin/main e411c3e58 and at this branch's head — identical in both directions:

ledger before after
MIRRORS pairs 158 158
KnownDrift 41 entries, 63 keys 41 entries, 63 keys
RuntimeOnlyDeclared 7 entries, 24 keys 7 entries, 24 keys
UnmirroredDeclared 14 entries, 86 keys 14 entries, 86 keys
WiderThanDeclared 22 entries, 35 keys 22 entries, 35 keys

No hand-reconciliation was performed and none was needed. The file's own header pins ("the ledger entry counts the header states are derived, not prose", and the key-total pin beside it) are green.

H3 — confirmed and collected. grid-columns-breakpoint-narrowing-8505.test.ts's last block asserted success: true for the xxl node as a handoff pin. It is flipped, not deleted. It also gained a positive control: with the flip, both readings in that block are now false, so on their own they would be equally green against a mirror that refuses everything — the caricature that file's own header warns about. The new row asserts { md: 2 } is still accepted, which is also where the z.enum overshoot lands.

What this PR does NOT close

These mirrors judge the document they are handed, and only that document. A grid nested under another node's children is reached through SchemaNodeSchema (base.zod.ts), a lazy union over the passthrough BaseSchemaCore that never re-enters the per-type arms — so the nested case is still accepted:

document safeValidateSchema
{ type: 'grid', columns: { xxl: 6 } } refused (this PR)
{ type: 'container', children: [ { type: 'grid', columns: { xxl: 6 } } ] } still accepted

Both rows were run, not reasoned about. This is pre-existing and untouched here — this PR neither widens nor narrows the nested path — and it is stated so that nobody reads this PR as having closed it.

⚠️ Sequencing hazard with PR #8553 — read this before merging

These two PRs touch the same ledger from opposite ends, and the collision is real. Stated as measured, ⛔ with no claim about which should land first — that is the dispatching seat's call, and it is recorded on #8553 as well.

  • On main today, neither key has a WiderThanDeclared entry. PR test(types): teach the WiderThanDeclared operator to see an open-record mirror #8553 is still open, so the pre-repair operator reads an index-signature record and a partial record over a key union as mutually assignable and reports both pairs as clean. Re-derived with the parity file's own AST instrument at origin/main and at this branch's head: WiderThanDeclared is 22 entries / 35 keys on both sides.
  • PR test(types): teach the WiderThanDeclared operator to see an open-record mirror #8553 adds entries claiming these two keys are wider. That is correct against main as it stands, and it is what that PR exists to make visible.
  • This PR removes the wideness. After it lands, the keys are no longer wider than their declarations, so an entry asserting that they are would be wrong.
  • Whichever lands second, the other's two rows are stale, and assertionDriftMatchesLedger reddens on them. It reddens in the merge queue, on the rebuilt candidate — so it ejects that PR from the queue rather than breaking main. The failure is loud and contained; it is not a silent landing.
  • The collision is not only bookkeeping — it is two live runtime assertions. test(types): teach the WiderThanDeclared operator to see an open-record mirror #8553 adds a describe block that runs the published validator on documents the published types refuse, and this PR flips both of its readings:
#8553 (head ca7a9cc74) it asserts after this PR
line 4078 GridSchema.safeParse({ type: 'grid', columns: { xxl: 6 } }).success is true false
line 4093 ReportComponentSchema accepts an xml export format refused

#8553 anticipated this in that block's own comment — it says the repair should redden there, where the ledger entry is, "instead of passing silently". So the collision is by design on its side, not a surprise; what it needs is a decision about order.

Firing controls

Run from the committed tree, each mutation proven on disk by counting the target and injected text in both directions, each restore proven by git diff HEAD empty and git hash-object byte-identity against the HEAD blobs.

ablation tsc -p tsconfig.test.json the two pin files
A — repair removed (mirrors back to the open record) exit 2, 5 errors exit 1, 7 tests failed
B — the FORBIDDEN z.record(z.enum([…]), …) spelling exit 2, 4 errors exit 1, 22 tests failed

⚠️ Ablation B caught one of this PR's own pins doing nothing, which is why its error count is 4 and not 3. _exportConfigsIsPartial was first written as an Eq over keyof of the two maps. keyof erases optionality, so keyof Record[F, V] and keyof Partial[Record[F, V]] are the same type, and the constant stayed green under the very overshoot its row is named for — it did not appear among B's errors, while its grid counterpart _columnsIsPartial, which compares the whole map type, did. It now compares the map to its own Partial, a fixed point only when the map is already partial, and it is line 133 in B's error list — shown red rather than asserted to work. A pin that cannot fail is worse than no pin, because it is counted as coverage; the file records the banned spelling beside the fixed one so it is not reintroduced.

Ablation B's load-bearing compile error is the fourth:

__tests__/zod-mirror-parity.test.ts(2370,14): error TS2322: Type 'LedgerMismatch' is not assignable to type 'never'.

That is assertionDriftMatchesLedger — the ledger reconciliation objectui#8556 ruled this must be pinned against rather than an accept set alone. The overshoot hazard is therefore verified on this tree, not inherited from objectui#8517's report. Its 22 runtime failures are exactly the per-member accepting rows: six breakpoints, five formats, the empty maps, and the flipped block's new positive control.

Bytes — the stated blocker, re-measured

#8516 said a zod narrowing here was unaffordable: the console framework chunk measured 70,999 gzip bytes against a 71,000 ceiling. PR #8550 landed the maintainer's raise — the ceiling is 100_000 against a re-derived 72_245 baseline, ~27.7 KB of headroom. PER_CHUNK_GZIP_CEILINGS and PER_CHUNK_BASELINE are untouched here.

The actual cost of this diff, measured on the two emitted modules (comment-stripped, gzip -9): +24 gzipped bytes (+63 raw), or 0.09% of the available headroom. Under the old ceiling it would indeed have been 24 bytes over — the blocker was real, and it is now gone. The authoritative gauge is CI's Bundle Analysis over the whole chunk.

Verification

what how result
@object-ui/types tests pnpm exec vitest run packages/types/ 148 files, 2837 tests, pass (re-run after the pin fix and after the merge)
@object-ui/types type-check pnpm --filter @object-ui/types type-check (all THREE programs; tsc --noEmit -p tsconfig.json alone is a known false green for this ledger) exit 0
the ledger is really in that program tsc -p tsconfig.test.json --listFiles 610 files, zod-mirror-parity.test.ts present
mirror consumers vitest run packages/cli/ packages/components/ packages/plugin-report/ examples/schema-catalog/ 303 files, 4765 tests, pass
consumer type-check turbo run type-check for cli, components, plugin-report 14 tasks, all successful
changeset gates check-changeset-presence.mjs, check-changeset-no-major.mjs exit 0, exit 0
check:control-bytes 6767 tracked text files scanned exit 0
check:spec-symbols exit 0
check:dist-completeness exit 0
check:self-import, check:esm-specifiers exit 0, exit 0
lint eslint --no-inline-config --format json over the 4 changed files 4 files, 0 errors, 0 warnings

Every exit code was captured by redirect before any pipe.

Merged, not rebased. origin/main was merged in at 1cca4415e; the two incoming commits touch packages/core and packages/app-shell and neither touches packages/typesgit diff over zod-mirror-parity.test.ts across the merge is empty. Re-run afterwards on the merged head: all three type-check programs exit 0, packages/types is 148 files / 2837 tests green, and packages/cli + packages/plugin-report are 31 files / 429 tests green. The ledger counts were re-derived after the merge and did not move — MIRRORS 158 pairs, KnownDrift 41/63, RuntimeOnlyDeclared 7/24, UnmirroredDeclared 14/86, WiderThanDeclared 22/35, identical to the pre-merge reading. Nothing was reconciled by hand.

Declared narrowing on lint. The repository-wide eslint . --no-inline-config is left to CI. The narrowing to 4 files is a measurement, not an omission: the population is read from eslint's own config, the count of 4 from its --format json output, and the invariance claim is that eslint.config.js enables no type-aware linting — it extends tseslint.configs.recommended, not recommendedTypeChecked, and its languageOptions declares no parserOptions.project or projectService. With no type information in scope, this diff cannot move the verdict on any file it did not touch.

check:readme-exports is NOT MEASURED, not red. It failed with 508 "type entry ./dist/index.d.ts is not on disk — run pnpm build first" against app-shell, auth, collaboration and other packages this diff does not touch; only @object-ui/types was built in this worktree. Its prerequisite is a full-repo build, which CI performs.

check:eager-closure is declared to CI. It reads apps/console/dist/eager-closure.json, which requires a console build; the byte arithmetic above stands in for it locally.

验收备注

Out-of-scope observations from this run. ⛔ None is filed as a card and none is repaired here.

  1. The PR test(types): teach the WiderThanDeclared operator to see an open-record mirror #8553 sequencing hazard has its own section above, because whoever merges this needs it without hunting for it.

  2. Now repaired in this PR, on a ruling. .changeset/8505-grid-columns-breakpoint-narrowing.md ended with three statements this PR falsified: that the zod mirror is deliberately not narrowed, the retired 70,999-against-71,000 byte reasoning, and a description of the handoff assertion as asserting acceptance. It was first reported here rather than edited unilaterally; the dispatching seat ruled it in scope, on the grounds that the changeset is unreleased and a PR that falsifies pending text owns correcting it. All three are corrected in a second commit, each naming fix(types): key the grid and report zod mirrors by their own declaration vocabulary (objectui#8516, objectui#8556) #8573 and quoting the new state rather than re-arguing it. The frontmatter and the file's other four paragraphs are byte-identical.

  3. check:readme-exports cannot run in a worktree that has not built every package (508 "type entry not on disk" reports against packages this diff does not touch). That is its documented prerequisite, not a defect; noted only so the NOT-MEASURED row in the table above is not read as a red gate.

Carrier: this PR's reviewer, plus whoever sequences #8553.

Scope

Six files, all inside the declared surface (the sixth is objectui#8505's changeset, corrected on the ruling recorded above). Contract review returned PASS on the contract path limb. No new export and no new error code, so Clause-② stays no: both repairs are pull-backs to already-published declarations and both accept sets get strictly narrower. Net +314 lines against the PM's ≤ 350 budget. @object-ui/layout, PER_CHUNK_GZIP_CEILINGS and PER_CHUNK_BASELINE were not touched.

⛔ Left as a draft deliberately: *.zod.ts is the contract path limb, so the pre-enqueue in-seat contract review is the dispatching seat's step, not this one's. needs:contract-review is not pre-hung.

Session: session_01CZY49skxUBYyJcdnTcYPrE

🤖 Generated with Claude Code

https://claude.ai/code/session_01CZY49skxUBYyJcdnTcYPrE


Generated by Claude Code

…, not by string

`GridSchema.columns` (objectui#8516) and `ReportComponentSchema.exportConfigs`
(objectui#8556) are one defect class: each mirror restated a CLOSED key set as
`z.record(z.string(), …)`, so `os-ui validate` / `check` passed documents the
published TypeScript face refuses, and the renderers then ignored the
out-of-vocabulary key silently.

Both narrow to `z.partialRecord`. NOT `z.record(z.enum([…]), …)`: measured on
zod 4.4.3, that spelling requires every member, so it would refuse the partial
map both declarations invite — trading each divergence for its opposite. The
measurement is pinned executably, at compile time (the inferred map is
`Partial<Record<…>>`, not `Record<…>`) and at run time (one accepting row per
member).

objectui#8505's handoff assertion is flipped, not deleted, and gains a positive
control so the pair cannot both read green against a mirror that refuses
everything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CZY49skxUBYyJcdnTcYPrE
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3475.6 KB 3512.7 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-BH00rO6J.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 498.93KB 114.12KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 10.12KB 3.28KB
data-objectstack (index.js) 192.72KB 53.55KB
fields (index.js) 243.24KB 61.42KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 49.00KB 13.91KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 213.21KB 43.63KB
plugin-detail (index.js) 248.46KB 63.90KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.30KB 56.63KB
plugin-kanban (index.js) 55.40KB 15.71KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.54KB 20.84KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…sified

Three statements in `.changeset/8505-grid-columns-breakpoint-narrowing.md` became
false because of this PR, and a PR that falsifies pending release text owns
correcting it. The changeset is unreleased, so this keeps pending text true rather
than rewriting a record.

Corrected, each naming #8573 as the cause: the zod mirror is no longer left open;
the 70,999-against-71,000 byte reasoning that deferred the narrowing is retired
by PR #8550's raise to 100,000 against a 72,245 baseline; and the handoff
assertion no longer asserts acceptance. Each correction quotes the new state
rather than re-arguing the measurement. Nothing else in the file is touched --
the frontmatter and the other four paragraphs are byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CZY49skxUBYyJcdnTcYPrE

os-sales commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

In-seat contract review — VERDICT: PASS (head 98e7aa8d)

⚠️ Notation. Generic type arguments are written with SQUARE brackets, per this PR's own convention.

Tier. Run as a CONTRACT_REVIEW_TIER subagent. Harness-stamped model in the review transcript: 97 stamps, all claude-fable-5-1, zero of anything else. ⛔ This seat is claude-opus-5 and did not and cannot self-clear the path limb; the verdict below is the reviewer's, carried verbatim and ⛔ not edited, abridged or softened by me.

Method. Two git archive extractions of the PR head under a scratchpad, frozen-lockfile install (zod 4.4.3, TS 6.0.3, vitest 4.1.10, spec 17.3.0). The shared checkout was only fetched; working tree untouched.

The central claim reproduced, with a containment sample

  • 357 columns values (14 keys × 11 value kinds, plus pairs) → 0 newly accepted, 175 newly refused.
  • 320 exportConfigs values → 0 newly accepted, 30 newly refused.
  • No new export, no new error code. invalid_key was already reachable in this repo and no non-test consumer switches on itunion-arm-diagnostics.ts:243,264 switch only on invalid_union/invalid_value; app-shell clientValidation.ts:866 special-cases only unrecognized_keys and validates spec documents, not these mirrors.
  • z.toJSONSchema on both new slots and on AnyComponentSchema does not throw; the emitted schema narrows (propertyNames: {enum: […]} where it was {type: string}).

Independently reproduced, ⛔ not taken from the body

The z.enum overshoot (five invalid_type issues on { md: 2 }xs, sm, lg, xl, 2xl); _columnsFace invariant by ablation in both directions, where a one-way extends would have been green in both; .passthrough() REFUSES rather than strips; z.partialRecord clones the key schema (zod/v4/classic/schemas.js:911-913) so the shared ReportExportFormatSchema keeps _zod.values and defaultExportFormat: 'xml' is still refused; both printed diagnostics; both ablations exactly (A: 5 compile errors at lines 88/96/104/111/120, 7 failed / 44 passed, ledger file green — H1's blindness confirmed on this tree; B: 3 errors incl. zod-mirror-parity.test.ts(2370,14), 22 failed / 29 passed); both restores byte-identical.

H1 verified. #8553 is open, merged: false, head ca7a9cc74 not an ancestor of main. At origin/main the operator (zod-mirror-parity.test.ts:557-562) is a one-way [mirror] extends [declared], satisfied by Record[string, V] ⇒ blind. Neither pair has an entry in any ledger. No entry should have moved.

Corpus spot-checked by the reviewer's own method (textual, brace-matched, both quote styles, every tracked file): objectui 7 pre-PR out-of-vocabulary sites exactly where the body says; objectstack a0856e3 has 0 grid nodes with object-valued columns and 0 exportConfigs. ⚠️ The reviewer counted 17 authored sites where the body says 28 — different counting, and "refuses zero documents that exist today" holds either way.

Not tried: bytes (+24 gz), eslint, check:readme-exports, check:control-bytes, check:spec-symbols, check:dist-completeness, consumer turbo type-check, the full components suite.

Non-blocking observations — ⛔ carried verbatim

  1. _exportConfigsIsPartial is inert against the overshoot it names (mirror-partial-record-narrowing-8516.test.ts:120). It compares keyof on both sides, and keyof Record[F,V] equals keyof Partial[Record[F,V]]; under ablation B it did not fire (only lines 88, 104 and ledger 2370 did). The header's sentence "the pins that catch the overshoot are … _exportConfigsIsPartial" is wrong for that pin, and under A it fires only for the key-set reason _exportKeys (line 111) already covers. The exportConfigs overshoot IS still caught (ledger reconciliation + six runtime rows), so not blocking. A pin that actually distinguishes: Eq[MirrorExportConfigs, Partial[MirrorExportConfigs]].
  2. "exportConfigs has zero authored inhabitants in either repository" is off by one: content/docs/core/report-schema.mdx:372-390 is an authored site keyed pdf/excel/csv — in-vocabulary, so nothing is refused, but body and changeset should say "one, in-vocabulary".
  3. Reach: the mirror judges only the ROOT document. safeValidateSchema({type:'container', children:[{type:'grid', columns:{xxl:6}}]})true, same for grid>children and page>body, because SchemaNodeSchema (base.zod.ts:53) is a lazy union over the passthrough BaseSchemaCore and never re-enters per-type arms. Pre-existing, not a widening, but "os-ui validate / check were passing documents tsc rejects" is closed only for root-level grid/report documents; worth one sentence so nobody reads this PR as closing the nested case.
  4. test(types): teach the WiderThanDeclared operator to see an open-record mirror #8553 sequencing is bidirectional at runtime too: beyond the two ledger entries the body names, test(types): teach the WiderThanDeclared operator to see an open-record mirror #8553's file asserts GridSchema.safeParse({type:'grid', columns:{xxl:6}}).success).toBe(true) (its line ~4078) which this PR flips. Whichever lands second reddens under both tsc and vitest; the queue catches it, as the body says.
  5. PR is mergeable_state: behind (main moved 2 commits past e411c3e58); a queue rebuild, not a contract issue.

⭐ Observation 3 is the one worth reading twice: it is not a defect in this PR, and it is the honest bound on what this PR closes. Observation 4 sharpens the sequencing hazard I posted on #8553 — the collision is a runtime assertion, not only two ledger rows.

Disposition. Path limb cleared. Observations 1–4 are being folded into the held push (a body/pin correction and two sentences), ⛔ not deferred: 1 and 2 are false statements in the PR's own text, and this PR is what has to carry them. Enqueue after that push is green.


Generated by Claude Code

…igures

Contract review found `_exportConfigsIsPartial` inert against the overshoot its
own row is named for: it compared `keyof` of the two maps, and `keyof` erases
optionality, so a total and a partial record over the same key set are the same
type there. Measured: under the forbidden-spelling ablation it did not appear
among the compile errors while its grid counterpart, which compares the whole map
type, did. It now compares the map to its own `Partial` -- a fixed point only when
the map is already partial -- and the header sentence says why `keyof` is banned
here, so the spelling is not reintroduced.

Two figures in the changeset were also wrong. `exportConfigs` was reported as
having zero authored inhabitants; it has one, `content/docs/core/report-schema.mdx`,
keyed pdf/excel/csv and so in vocabulary. The scan that produced the zero could
not have found a true positive: its pattern forbade nested braces, and every real
`exportConfigs` value contains per-format objects. The `columns` count is restated
with test fixtures separated from authored sites rather than folded in. Neither
correction moves the conclusion -- zero documents that exist today are refused.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CZY49skxUBYyJcdnTcYPrE
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3475.6 KB 3512.7 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-BH00rO6J.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 498.93KB 114.12KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 10.12KB 3.28KB
data-objectstack (index.js) 192.72KB 53.55KB
fields (index.js) 243.24KB 61.42KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 49.00KB 13.91KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 213.21KB 43.63KB
plugin-detail (index.js) 248.46KB 63.90KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.30KB 56.63KB
plugin-kanban (index.js) 55.40KB 15.71KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.54KB 20.84KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

os-sales commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Landing check ② is UNSATISFIABLE here — recorded, ⛔ not passed silently, ⛔ not bought with a false declaration

Dispatching seat. This PR is green on ① and ③ and not enqueued, because ② cannot be satisfied without writing something untrue.

check state
① in-seat fable contract review PASS (claude-fable-5-1, 97 transcript stamps). Contract surface is byte-identical between the reviewed head 98e7aa8d and the current head c77dfeb6dgit diff over layout.zod.ts + reports.zod.ts across that range is empty, so the verdict still covers this head.
check-clause2-carriers --pair 8573 exit 4, and it cannot reach 0 — see below
③ CI + mergeability ✅ 33/33 (30 success, 3 skipped, 0 failing), mergeable_state clean

C2 is fixed. Both cards were missing the declaration line in the carrier the gate actually reads. #8556's packed pointer comment carried no Clause-②: line at all, and #8516's explanation was on the thread rather than in the claim. Both now carry a re-stated claim comment (same session, same branch, ⛔ not a re-claim). ⚠️ Packing lesson: an anchor's full claim does not carry a packed card's declaration — every card in a pack needs its own line.

C5 cannot be cleared, and that is a property of the tell, not of this PR. It fires on the CONSTRUCTOR and has no notion of direction:

  • layout.zod.ts:240z.partialRecord(z.enum([six]), z.number()) replaces z.record(z.string(), z.number()). Six named keys is strictly fewer than every string.
  • reports.zod.ts:173exportConfigs is not a new key; the line is a rewrite of an existing one.

The contract review measured the actual direction: 357 columns values → 0 newly accepted, 175 newly refused; 320 exportConfigs values → 0 newly accepted, 30 newly refused. Both accept sets strictly narrower, no new export, no new error code.

⇒ The tell says "wider", the measurement says "narrower", and the checker offers exactly one route to exit 0: re-declare Clause-②: yes. ⛔ That would be a false declaration and this seat will not write one to buy a green. The tell's own docblock says it is "A TELL, never a proof, in BOTH directions"; its direction-blindness is filed as objectstack#16822.

Disposition. PR stays a draft, ⛔ not enqueued by this seat. Everything a merger needs is green and measured; the one red is a checker that cannot express "narrower". Whoever enqueues this should do so knowing ② is red for that reason and ⛔ not because the declaration is wrong.


Generated by Claude Code

@os-sales
os-sales marked this pull request as ready for review September 8, 2026 14:23

os-sales commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Enqueued with ② red — the decision, and whose it is

Dispatching seat, session_01CZY49skxUBYyJcdnTcYPrE. Head c77dfeb6d. Marked ready for review and auto-merge (SQUASH) enabled, which in this repo is the enqueue action — the ruleset forces the merge queue and a direct merge is rejected 405.

Not a governed surface, verified rather than assumed: check-governed-queue-guard.mjs --test over all four changed paths → "NOT GOVERNED — 4 path(s) checked against 5 governed surface(s); none matched." ⚠️ Note the CI check of the same name is green on every PR — its pull_request leg deliberately exits 0, so ⛔ green there is not evidence of not-governed. The --test run above is.

Landing checks:

check result
① in-seat contract review PASS (claude-fable-5-1, 97 transcript stamps). Contract surface byte-identical between the reviewed head 98e7aa8d and c77dfeb6d. All four of its findings folded in and the replacement pin proven to fire under ablation B (now line 133, B went 3 errors → 4).
--pair 8573 exit 4, and structurally unsatisfiable
③ CI + mergeability ✅ 33/33 (30 success, 3 skipped, 0 failing), mergeable_state clean

On ②, and I am recording this as my call rather than leaving it implicit. The C5 tell fires on the constructor and has no notion of direction: both flagged lines are replacementsz.record(z.string(), …)z.partialRecord(…) — so the accept sets went from every string key to six named breakpoints and five declared formats. The only route to exit 0 is re-declaring Clause-②: yes. ⛔ That would be false, and this seat will not write a false declaration to buy a green.

What ② exists to protect has been checked by a stronger instrument: the contract review measured the actual direction — 357 columns values → 0 newly accepted, 175 newly refused; 320 exportConfigs values → 0 newly accepted, 30 newly refused, no new export, no new error code. ⇒ Landing on a fable PASS with a diagnosed, filed (objectstack#16822) checker false positive is the honest disposition; holding a correct narrowing forever behind an instrument that cannot express "narrower" is not.

⚠️ Reversible, and here is how: turning the PR back to draft is the only reliable way to exit the queue (disable_pr_auto_merge alone drops auto-merge but ⛔ does not cancel queue membership — both are needed). If a maintainer disagrees with landing on a red ②, do that and this stands down.

⚠️ Sequencing still live: this collides both ways with PR #8553 — two ledger rows and two runtime assertions (its ~4078/4093) that this PR flips. The queue rebuilds on current main, so the second one through ejects rather than lands. Posted there; ⛔ no order claimed by this seat.


Generated by Claude Code

@os-sales
os-sales added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit d4733f2 Sep 8, 2026
35 checks passed
@os-sales
os-sales deleted the claude/issue-8516-mirror-partial-record-narrowing branch September 8, 2026 14:40
os-justin pushed a commit that referenced this pull request Sep 8, 2026
Brings 27 commits of `main` onto this branch and resolves the one conflicting
path, `packages/types/src/__tests__/zod-mirror-parity.test.ts`. Both sides had
rewritten the same header bullet for `WiderThanDeclared`.

Kept both intents:

  * main's objectui#8338 retirement of `feedback.zod.ts#ToastSchema::action`
    (its ledger entry, arm row and header history), objectui#8248's LIVE /
    HISTORICAL ruling and objectui#8458's movement pin;
  * this branch's OPEN-RECORD operator clause, its synthetic recognition pins
    and the caricature negative.

What the merge falsified, measured rather than assumed. PR #8573
(`d4733f27e`, objectui#8516 / objectui#8556) narrowed BOTH mirrors this card
ledgered — `GridSchema.columns` and `ReportComponentSchema.exportConfigs` — to
`z.partialRecord`, so the operator reports neither on the merged tree. The two
ledger entries and their arm rows are removed (with the entries in place,
`tsc -p tsconfig.test.json` reddens at `assertionWiderMatchesLedger` and
`assertionWiderLedgerRecordsEveryKey`), and the runtime describe block that
asserted `safeParse({ columns: { xxl: 6 } })` returns green is removed because
it no longer does — that reachability is now pinned, inverted, by main's own
`mirror-partial-record-narrowing-8516.test.ts`.

Every header figure is re-derived from the tree by the file's own pins, not
stepped by hand: `WiderThanDeclared` reads 22 / 35 / 45 — 6 / 29 / 0 / 10,
which is main's reading unchanged, because the clause moves no live key today.
Three prose figures that the merge left standing without an instrument are
anchored to the revision they were read at, and one bare count no pin reached
is removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
os-justin pushed a commit that referenced this pull request Sep 8, 2026
The `WiderArmClass` docblock listed OPEN-RECORD among the sub-classes "the
docblocks above name", which was true while this branch carried two ledger rows
for it. PR #8573 narrowed both mirrors, the rows are gone, and no docblock above
names one — so the sentence was a sub-class name with nothing behind it, which
is the exact rot this file exists to catch.

Reverts the list to what `main` has and records why OPEN-RECORD is absent, so
the next reader does not "restore" it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment